lucky

lucky

跟着好奇心,去探索我觉得酷的东西 希望以电子报分享所见所思的方式,结识更多朋友,碰撞出更多思维火花
github
bilibili
twitter
medium
youtube
zhihu
mastodon
follow
substack

タイプスクリプト

公式サイト#

基本型#

ブール値#

最も基本的なデータ型は単純な true/false 値で、JavaScript と TypeScript ではbooleanと呼ばれます(他の言語でも同様です)。

let isDone: boolean = false;

数字#

JavaScript と同様に、TypeScript のすべての数字は浮動小数点数です。これらの浮動小数点数の型はnumberです。TypeScript は 10 進数と 16 進数のリテラルをサポートするだけでなく、ECMAScript 2015 で導入された 2 進数と 8 進数のリテラルもサポートしています。

let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
let binaryLiteral: number = 0b1010;
let octalLiteral: number = 0o744;

文字列#

JavaScript プログラムのもう一つの基本的な操作は、ウェブページやサーバーサイドのテキストデータを処理することです。他の言語と同様に、stringを使用してテキストデータ型を表します。JavaScript と同様に、ダブルクォーテーション(")またはシングルクォーテーション(')を使用して文字列を表すことができます。

let name: string = "bob";
name = "smith";

また、テンプレート文字列を使用することもできます。これは複数行のテキストと埋め込み式を定義できます。この文字列はバッククォート(`)で囲まれ、${ expr }の形式で式を埋め込むことができます。

let name: string = `Gene`;
let age: number = 37;
let sentence: string = `Hello, my name is ${ name }.

I'll be ${ age + 1 } years old next month.`;

これは以下のようにsentenceを定義する方法と同じ効果があります:

let sentence: string = "Hello, my name is " + name + ".\n\n" +
    "I'll be " + (age + 1) + " years old next month.";

配列#

TypeScript は JavaScript と同様に配列要素を操作できます。配列を定義する方法は 2 つあります。最初の方法は、要素の型の後に[]を付けて、その型の要素からなる配列を表します:

let list: number[] = [1, 2, 3];

2 つ目の方法は配列のジェネリックを使用することで、Array <要素型>を使用します:

let list: Array<number> = [1, 2, 3];

タプル#

タプル型は、既知の要素数と型の配列を表すことを許可します。各要素の型は同じである必要はありません。たとえば、stringnumber型のペアを定義できます。

// タプル型を宣言
let x: [string, number];
// 初期化
x = ['hello', 10]; // OK
// 不正に初期化
x = [10, 'hello']; // エラー

既知のインデックスの要素にアクセスすると、正しい型が得られます:

console.log(x[0].substr(1)); // OK
console.log(x[1].substr(1)); // エラー、'number'には'substr'がありません

範囲外の要素にアクセスすると、ユニオン型が代わりに使用されます:

x[3] = 'world'; // OK、文字列は(string | number)型に代入できます

console.log(x[5].toString()); // OK、'string'と'number'はどちらもtoStringを持っています

x[6] = true; // エラー、ブール値は(string | number)型ではありません

ユニオン型は高度なテーマであり、後の章で説明します。

列挙型#

enum型は JavaScript の標準データ型を補完するものです。C# などの他の言語と同様に、列挙型を使用すると、一連の数値に親しみやすい名前を付けることができます。

enum Color {Red, Green, Blue}
let c: Color = Color.Green;

デフォルトでは、0から要素に番号が付けられます。また、メンバーの数値を手動で指定することもできます。たとえば、上記の例を1から番号を付けるように変更できます:

enum Color {Red = 1, Green, Blue}
let c: Color = Color.Green;

または、すべて手動で値を割り当てることもできます:

enum Color {Red = 1, Green = 2, Blue = 4}
let c: Color = Color.Green;

列挙型の便利な点は、列挙の値からその名前を取得できることです。たとえば、数値が 2 であることはわかっていますが、それが Color のどの名前にマッピングされているかは不明な場合、対応する名前を検索できます:

enum Color {Red = 1, Green, Blue}
let colorName: string = Color[2];

console.log(colorName);  // 'Green'と表示されます。上記のコードではその値は2です

任意の型#

時には、プログラミング段階で型が不明な変数に型を指定したいことがあります。これらの値は、ユーザー入力やサードパーティのコードライブラリからの動的な内容から来る可能性があります。この場合、型チェッカーがこれらの値をチェックすることを望まず、コンパイル段階のチェックを通過させたいと考えています。そのため、any型を使用してこれらの変数にマークを付けることができます:

let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // OK、確実にブール値

既存のコードを改修する際に、any型は非常に便利です。これにより、コンパイル時に型チェックを選択的に含めたり除外したりできます。あなたはObjectが他の言語のように似た役割を持っていると思うかもしれません。しかし、Object型の変数は任意の値を割り当てることを許可しますが、実際にそのメソッドが存在していても、任意のメソッドを呼び出すことはできません:

let notSure: any = 4;
notSure.ifItExists(); // OK、ifItExistsはランタイムで存在するかもしれません
notSure.toFixed(); // OK、toFixedは存在します(しかしコンパイラはチェックしません)

let prettySure: Object = 4;
prettySure.toFixed(); // エラー:プロパティ'toFixed'は型'Object'には存在しません。

データの型の一部しかわからない場合、any型も便利です。たとえば、異なる型のデータを含む配列があります:

let list: any[] = [1, true, "free"];

list[1] = 100;

void 無返却値#

ある意味で、void型はany型とは反対のようなもので、何の型もないことを示します。関数が返却値を持たない場合、通常その返却値の型はvoidです:

function warnUser(): void {
    console.log("This is my warning message");
}

void 型の変数を宣言することにはあまり意味がありません。なぜなら、あなたはそれにundefinednullだけを割り当てることができるからです:

let unusable: void = undefined;

Null と Undefined#

TypeScript では、undefinednullはそれぞれ独自の型を持ち、undefinednullと呼ばれます。 voidと同様に、それらの本来の型の用途はあまり大きくありません:

// これらの変数に割り当てることができるものはあまりありません!
let u: undefined = undefined;
let n: null = null;

デフォルトでは、nullundefinedはすべての型のサブタイプです。つまり、 nullundefinednumber型の変数に割り当てることができます。

しかし、--strictNullChecksフラグを指定すると、nullundefinedvoidとそれぞれにのみ割り当てることができます。これにより、多くの一般的な問題を回避できます。どこかでstringまたはnullまたはundefinedを渡したい場合、ユニオン型string | null | undefinedを使用できます。再度言いますが、ユニオン型については後で説明します。

注意:できるだけ--strictNullChecksを使用することをお勧めしますが、このマニュアルではこのフラグがオフになっていると仮定しています。

Never#

never型は永遠に存在しない値の型を表します。たとえば、 never型は常に例外をスローするか、まったく返却値を持たない関数式やアロー関数式の返却値の型です。また、変数も never型である可能性があります。これは、それらが決して真にならない型保護によって制約される場合です。

never型はすべての型のサブタイプであり、任意の型に割り当てることができます。ただし、どの型もneverのサブタイプであるか、never型に割り当てることができる(never自身を除いて)ことはありません。たとえ anyでさえ、neverに割り当てることはできません。

以下は、never型を返す関数の例です:

// neverを返す関数は到達不可能な終点を持たなければなりません
function error(message: string): never {
    throw new Error(message);
}

// 推論された返却値の型はneverです
function fail() {
    return error("Something failed");
}

// neverを返す関数は到達不可能な終点を持たなければなりません
function infiniteLoop(): never {
    while (true) {
    }
}

Object オブジェクト#

objectは非原始型を表し、すなわちnumberstringbooleansymbolnull、またはundefined以外の型を表します。

使用することでobject型は、Object.createのような API をより良く表現できます。たとえば:

declare function create(o: object | null): void;

create({ prop: 0 }); // OK
create(null); // OK

create(42); // エラー
create("string"); // エラー
create(false); // エラー
create(undefined); // エラー

型アサーション#

時には、あなたが TypeScript よりも特定の値についての詳細を知っている状況に遭遇することがあります。通常、これはあなたがあるエンティティがその既存の型よりも正確な型を持っていることを明確に知っているときに発生します。

型アサーションを使用することで、コンパイラに「信じてください、私は自分が何をしているか知っています」と伝えることができます。型アサーションは他の言語の型変換に似ていますが、特別なデータチェックや解構は行いません。実行時の影響はなく、コンパイル段階でのみ機能します。TypeScript は、あなたが必要なチェックを行ったと仮定します。

型アサーションには 2 つの形式があります。1 つは「山括弧」構文です:

let someValue: any = "this is a string";

let strLength: number = (<string>someValue).length;

もう 1 つはas構文です:

let someValue: any = "this is a string";

let strLength: number = (someValue as string).length;

インターフェース#

イントロダクション#

TypeScript のコア原則の 1 つは、値が持つ構造に対して型チェックを行うことです。これは時々「ダックタイピング」または「構造的サブタイピング」と呼ばれます。TypeScript では、インターフェースの役割はこれらの型に名前を付け、あなたのコードやサードパーティのコードの契約を定義することです。

インターフェースの初探#

以下は、インターフェースがどのように機能するかを示す簡単な例です。

function printLabel(labelledObj: { label: string }) {
  console.log(labelledObj.label);
}

let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);

型チェック器はprintLabelの呼び出しを確認します。 printLabelは 1 つのパラメータを持ち、このオブジェクトパラメータにはlabelという名前のプロパティがstring型で必要です。注意すべきは、渡されたオブジェクトパラメータには実際には多くのプロパティが含まれていますが、コンパイラは必須のプロパティが存在し、その型が一致しているかどうかのみを確認することです。しかし、時には TypeScript はそれほど寛容ではありません。これについては後で少し説明します。

次に、上記の例を再構築し、インターフェースを使用して、必ず含まれるlabelプロパティがstring型であることを示します:

interface LabelledValue {
  label: string;
}

function printLabel(labelledObj: LabelledValue) {
  console.log(labelledObj.label);
}

let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);

LabelledValueインターフェースは、上記の例の要件を説明するための名前のようなものです。これは、 labelプロパティがstring型のオブジェクトを表します。注意すべきは、ここでは他の言語のように、 printLabelに渡されるオブジェクトがこのインターフェースを実装しているとは言えません。私たちは値の形状にのみ注目します。渡されたオブジェクトが上記の必要条件を満たしていれば、それは許可されます。

もう一つの重要な点は、型チェック器はプロパティの順序を確認しないことです。必要なプロパティが存在し、型が正しければ問題ありません。

オプショナルプロパティ#

インターフェースのプロパティは必ずしもすべて必須ではありません。いくつかは特定の条件下でのみ存在するか、まったく存在しない場合があります。オプショナルプロパティは「オプションバッグ」パターンを適用する際に非常に一般的です。これは、関数に渡されるパラメータオブジェクトの一部のプロパティのみが設定されている場合です。

以下は「オプションバッグ」を適用した例です:

interface SquareConfig {
  color?: string;
  width?: number;
}

function createSquare(config: SquareConfig): {color: string; area: number} {
  let newSquare = {color: "white", area: 100};
  if (config.color) {
    newSquare.color = config.color;
  }
  if (config.width) {
    newSquare.area = config.width * config.width;
  }
  return newSquare;
}

let mySquare = createSquare({color: "black"});

オプショナルプロパティを持つインターフェースは、通常のインターフェース定義とほぼ同じですが、オプショナルプロパティ名の後に?シンボルを追加します。

オプショナルプロパティの利点の 1 つは、存在する可能性のあるプロパティを事前に定義できることです。もう 1 つの利点は、存在しないプロパティを参照したときのエラーをキャッチできることです。たとえば、createSquare内のcolorプロパティ名を誤って記述すると、エラーメッセージが表示されます:

interface SquareConfig {
  color?: string;
  width?: number;
}

function createSquare(config: SquareConfig): { color: string; area: number } {
  let newSquare = {color: "white", area: 100};
  if (config.clor) {
    // エラー:プロパティ'clor'は型'SquareConfig'には存在しません
    newSquare.color = config.clor;
  }
  if (config.width) {
    newSquare.area = config.width * config.width;
  }
  return newSquare;
}

let mySquare = createSquare({color: "black"});

読み取り専用プロパティ#

いくつかのオブジェクトプロパティは、オブジェクトが作成されたときにのみその値を変更できます。プロパティ名の前にreadonlyを付けることで、読み取り専用プロパティを指定できます:

interface Point {
    readonly x: number;
    readonly y: number;
}

オブジェクトリテラルを割り当てることでPointを構築できます。割り当てた後、xyは変更できなくなります。

let p1: Point = { x: 10, y: 20 };
p1.x = 5; // エラー!

TypeScript にはReadonlyArray型があり、これはArrayに似ていますが、すべての可変メソッドが削除されているため、配列が作成された後に変更できないことを保証します:

let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // エラー!
ro.push(5); // エラー!
ro.length = 100; // エラー!
a = ro; // エラー!

上記のコードの最後の行では、全体のReadonlyArrayを通常の配列に割り当てることはできないことがわかります。しかし、型アサーションを使用して書き換えることができます:

a = ro as number[];

readonly vs const#

最も簡単にreadonlyconstを判断する方法は、それを変数として使用するか、プロパティとして使用するかを確認することです。変数として使用する場合はconstを使用し、プロパティとして使用する場合はreadonlyを使用します。

追加のプロパティチェック#

最初の例でインターフェースを使用したとき、TypeScript は{ size: number; label: string; }を渡すことを許可しましたが、これは{ label: string; }のみを期待している関数に渡されました。オプショナルプロパティについて学び、これらが「オプションバッグ」パターンで非常に便利であることを知っています。

しかし、無邪気にこれら二つを組み合わせると、JavaScript のように自分の足を打つことになります。たとえば、createSquareの例を考えてみましょう:

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。